home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d12
/
ddj0290.arc
/
SCHULMAN.LST
< prev
next >
Wrap
File List
|
1990-01-07
|
21KB
|
705 lines
_STALKING GENERAL PROTECTION FAULTS: PART II_
by Andrew Schulman
NOTE: LISTINGS ONE THROUGH THREE WERE PUBLISHED IN THE JANUARY
1990 ISSUE OF DDJ AND ON-LINE LISTINGS CAN BE FOUND IN THAT AREA
[LISTING FOUR]
/* GPF386.C -- for Phar Lap 386|DOS-Extender and Watcom C 386 7.0
wcl386 -DPROT_MODE -3r -mf -Ox -s gpf386
wdisasm gpf386 > gpf386.asm
run386 gpf386
NOTE! To keep this example short, most of the precautions taken
in Listing 3 are not repeated here. Refer to Listing 3 (GPFAULT.C)
for how the interrupt handler should really be written. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <setjmp.h>
#include <dos.h>
#include "gpfault.h"
void reset_pharlap(void);
void prot_far *getvect_prot(short intno);
void real_far *getvect_real(short intno);
BOOL setvect_prot(short intno, void prot_far *handler);
BOOL setvect_real(short intno, void real_far *handler);
BOOL setvect(short intno, void prot_far *handler);
BOOL set2vect(short intno, void prot_far *phandler, void real_far *rhandler);
void revert(void); // reinstall default handler
void interrupt far int13handler(REG_PARAMS r);
unsigned xtoi(char *s);
void prot_far *old_int13handler_prot;
void real_far *old_int13handler_real;
jmp_buf toplevel;
unsigned legal = 0; // just a legal address to bang on
BOOL in_user_code = FALSE;
#define USE16 0x66
#define INT 0xcd
#define PUSH_DS 0x1e
#define POP_DS 0x1f
#define MOV_AX USE16 0xb8
#define MOV_DS_ES 0x8c 0xc0 0x8e 0xd8 // MOV AX,ES/MOV DS,AX
#define XOR_AL 0x34
#define MOV_AX_CARRYFL 0x9c 0x58 XOR_AL 0x01 // PUSHF/POP AX/XOR AL,1
#define STI 0xfb
/* directives for compiler to generate inline code */
#pragma aux reset_pharlap = MOV_AX 0x01 0x25 /* 0x2501 */ INT 0x21 ;
#pragma aux getvect_prot = MOV_AX 0x02 0x25 /* 0x2502 */ INT 0x21 \
parm [cl] value [ebx es] ;
/*
explanation of #pragma aux: the preceding says that getvect_prot()
takes one parameter in CL register, and returns value in ES:EBX.
The "function" itself sets AX to 0x2502 and does an INT 0x21.
When called: old_int13handler_prot = getvect_prot(0x0D);
the compiler generates:
mov cl, 0dh
mov ax, 2502h
int 21h
mov old_int13handler_prot+4, es
mov old_int13handler_prot, ebx
*/
#pragma aux getvect_real = MOV_AX 0x03 0x25 /* 0x2503 */ INT 0x21 \
parm [cl] value [ebx] ;
#pragma aux setvect_real = \
MOV_AX 0x05 0x25 /* 0x2505 */ \
INT 0x21 \
MOV_AX_CARRYFL \
parm [cl] [ebx] value ;
#pragma aux setvect_prot = \
PUSH_DS \
MOV_DS_ES \
MOV_AX 0x04 0x25 /* 0x2504 */ \
INT 0x21 \
POP_DS \
MOV_AX_CARRYFL \
parm [cl] [es edx] value ;
#pragma aux setvect = \
PUSH_DS \
MOV_DS_ES \
MOV_AX 0x06 0x25 /* 0x2506 */ \
INT 0x21 \
POP_DS \
MOV_AX_CARRYFL \
parm [cl] [es edx] value ;
#pragma aux set2vect = \
PUSH_DS \
MOV_DS_ES \
MOV_AX 0x07 0x25 /* 0x2507 */ \
INT 0x21 \
POP_DS \
MOV_AX_CARRYFL \
parm [cl] [es edx] [ebx] value ;
main()
{
char buf[255];
unsigned prot_far *fp;
unsigned short seg;
unsigned off, data;
old_int13handler_real = getvect_real(0x0D);
old_int13handler_prot = getvect_prot(0x0D);
setvect(0x0D, (void prot_far *) int13handler);
printf("'Q' to quit, '!' to reinstall default GP Fault handler\n");
printf("%Fp is a legal address to poke\n", (void far *) &legal);
printf("%Fp is not a legal address to poke\n", (void far *) (&legal-1));
setjmp(toplevel);
for (;;)
{
printf("$ ");
*buf = '\0';
gets(buf);
if (toupper(*buf) == 'Q')
break;
else if (*buf == '!')
{
revert();
continue;
}
// got bored of using sscanf()
seg = xtoi(strtok(buf, ": \t"));
off = xtoi(strtok(0, " \t"));
data = xtoi(strtok(0, " \t"));
in_user_code = TRUE;
fp = MK_FP(seg, off); // is this really user code?
*fp = data;
printf("poked %Fp with %x\n", fp, *fp);
in_user_code = FALSE;
}
revert();
printf("Bye\n");
return 0;
}
void revert(void)
{
if (! set2vect(0x0d, old_int13handler_prot, old_int13handler_real))
printf("Can't revert!\n");
}
void interrupt far int13handler(REG_PARAMS r)
{
_enable();
reset_pharlap();
if (in_user_code)
{
in_user_code = FALSE;
printf("\nProtection violation at %04X:%08X\n",
r.cs, r.ip);
if (r.err_code)
printf("Error code %04X\n", r.err_code);
printf("<DS %04X> <ES %04X> <FS %04X> <GS %04X>\n",
r.ds, r.es, r.fs, r.gs);
printf("<EDI %08X> <ESI %08X> <FLAGS %08X>\n",
r.di, r.si, r.flags);
printf("<EAX %08X> <EBX %08X> <ECX %08X> <EDX %08X>\n",
r.ax, r.bx, r.cx, r.dx);
longjmp(toplevel, -1);
/*NOTREACHED*/
}
else
{
printf("An internal error has occurred at %04X:%08X\n",
r.cs, r.ip);
revert();
_chain_intr(old_int13handler_prot);
/*NOTREACHED*/
}
}
// convert ASCIIZ hex string to integer
unsigned xtoi(char *s)
{
unsigned i =0, t;
while (*s == ' ' || *s == '\t') s++;
for (;;)
{
char c = *s;
if (c >= '0' && c <= '9') t = 48;
else if (c >= 'A' && c <= 'F') t = 55;
else if (c >= 'a' && c <= 'f') t = 87;
else break;
i = (i << 4) + (c - t);
s++;
}
return i;
}
[LISTING FIVE]
/* GPF386.C -- for Phar Lap 386|DOS-Extender and MetaWare High C for 386 MS-DOS
set ipath=\c386\inc\
\c386\hc386 gpfault -define PROT_MODE
\pharlap\fastlink gpfault -lib small\hce.lib
\pharlap\run386 gpfault
NOTE! To keep this example short, most of the precautions taken
in Listing 3 are not repeated here. Refer to Listing 3 (GPFAULT.C)
for how the interrupt handler should really be written.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <setjmp.h>
#include "msdos.cf"
#include "interrup.cf"
#include "gpfault.h"
BOOL call_pharlap(unsigned short ax, unsigned short cl);
void reset_pharlap(void);
IPROC getvect_prot(short intno);
void real_far *getvect_real(short intno);
BOOL setvect_prot(short intno, void prot_far *handler);
BOOL setvect_real(short intno, void real_far *handler);
BOOL setvect(short intno, IPROC handler);
BOOL set2vect(short intno, IPROC phandler, void real_far *rhandler);
void revert(void); /* install old handlers */
#pragma Calling_convention(C_interrupt | _FAR_CALL);
void int13handler(REG_PARAMS r);
#pragma Calling_convention();
IPROC old_int13handler_prot;
void real_far *old_int13handler_real;
jmp_buf toplevel;
REG_PARAMS r2 = {0};
BOOL in_user_code = FALSE;
main()
{
char buf[255];
unsigned prot_far *fp;
unsigned short seg;
unsigned off, data;
old_int13handler_real = getvect_real(0x0D);
old_int13handler_prot = getvect_prot(0x0D);
setvect(INT_GPFAULT, int13handler);
printf("'Q' to quit, '!' to reinstall default GP Fault handler\n");
if (setjmp(toplevel) == -1)
{
printf("Protection violation at %04X:%08X\n",
r2.cs, r2.ip);
if (r2.err_code)
printf("Error code %04X\n", r2.err_code);
printf("<ES %04X> <DS %04X> <EDI %08X> <ESI %08X> <FLAGS %08X>\n",
r2.es, r2.ds, r2.di, r2.si, r2.flags);
printf("<EAX %08X> <EBX %08X> <ECX %08X> <EDX %08X>\n",
r2.ax, r2.bx, r2.cx, r2.dx);
}
for (;;)
{
printf("$ ");
*buf = '\0';
gets(buf);
if (toupper(*buf) == 'Q')
break;
else if (*buf == '!')
{
revert();
continue;
}
sscanf(buf, "%04X:%08X %x", &seg, &off, &data);
FP_SEG(fp) = seg;
FP_OFF(fp) = off;
in_user_code = TRUE;
*fp = data;
printf("poked %p with %x\n", fp, *fp);
in_user_code = FALSE;
}
revert();
printf("Bye\n");
return 0;
}
void revert(void)
{
set2vect(0x0d, old_int13handler_prot, old_int13handler_real);
}
#pragma Calling_convention(C_interrupt | _FAR_CALL);
void int13handler(REG_PARAMS r)
{
if (in_user_code)
{
in_user_code = FALSE;
r2 = r;
reset_pharlap();
STI; // _inline(0xFB): reenable interrupts
longjmp(toplevel, -1);
}
else
{
printf("Internal error at %04X:%08X\n", r.cs, r.ip);
revert();
return; // let the fault happen again (no _chain_intr)
}
/*NOTREACHED*/
}
#pragma Calling_convention();
BOOL call_pharlap(unsigned short ax, unsigned short cl)
{
Registers.AX.W = ax;
Registers.CX.LH.L = cl;
calldos();
return !(Registers.Flags & 1);
}
void reset_pharlap(void)
{
call_pharlap(0x2501, 0);
}
IPROC getvect_prot(short intno)
{
IPROC handler;
call_pharlap(0x2502, intno);
/* no MK_FP for High C 386 */
FP_SEG(handler) = Registers.ES.W;
FP_OFF(handler) = Registers.BX.R;
return handler;
}
void real_far *getvect_real(short intno)
{
call_pharlap(0x2503, intno);
return (void real_far *) Registers.BX.R;
}
BOOL setvect_prot(short intno, void prot_far *handler)
{
Registers.DS.W = FP_SEG(handler);
Registers.DX.R = FP_OFF(handler);
return call_pharlap(0x2504, intno);
}
BOOL setvect_real(short intno, void real_far *handler)
{
Registers.BX.R = (unsigned) handler;
return call_pharlap(0x2505, intno);
}
BOOL setvect(short intno, IPROC handler)
{
Registers.DS.W = FP_SEG(handler);
Registers.DX.R = FP_OFF(handler);
return call_pharlap(0x2506, intno);
}
BOOL set2vect(short intno, IPROC phandler, void real_far *rhandler)
{
Registers.DS.W = FP_SEG(phandler);
Registers.DX.R = FP_OFF(phandler);
Registers.BX.R = (unsigned) rhandler;
return call_pharlap(0x2507, intno);
}
[LISTING SIX]
/* OS2TRACE.C -- catching GP faults in OS/2, using DosPTrace()
compile with:
cl -Lp os2trace.c
to make tiny (less than 3K) .EXE with C run-time DLL, compile with:
cl -AL -c -Gs2 -Ox -Lp -I\msc\inc\mt os2trace.c
link /nod/noi crtexe.obj os2trace,os2trace,,crtlib.lib \os2\lib\os2.lib;
*/
#include <stdio.h>
#include <string.h>
#include <process.h>
#include <signal.h>
#include <setjmp.h>
#define INCL_DOS
#define INCL_DOSTRACE
#define INCL_VIO
#include "os2.h"
#include "ptrace.h"
#define LOCAL static
typedef unsigned WORD;
LOCAL VOID NEAR print_regs(void);
LOCAL VOID NEAR send_sig_segv(void);
LOCAL VOID NEAR catch_sig_segv(void);
LOCAL WORD NEAR trace(int argc, char *argv[], BOOL verbose);
LOCAL VOID NEAR start_trace(char *prog, char *cmdline);
LOCAL char *progname(char *s);
LOCAL char *cmdline(int argc, char *argv[]);
LOCAL WORD NEAR selector_from_segment(WORD seg);
LOCAL VOID NEAR set_csip(WORD cs, WORD ip);
LOCAL int break_handler(void);
PTRACEBUF ptb;
jmp_buf toplevel;
#define FP_OFF(fp) ((unsigned) (fp))
#define JMP_GPFAULT -1
#define JMP_BREAK -2
main(int argc, char *argv[])
{
char buf[80];
BOOL do_trace = TRUE;
BOOL verbose = FALSE;
WORD x;
int i;
for (i=1; i<argc; i++)
if (argv[i][0] == '-')
switch(argv[i][1])
{
case 'x': do_trace = FALSE; break;
case 'v': verbose = TRUE; break;
}
signal(SIGINT, break_handler); // doesn't work with CRTLIB.DLL
if (do_trace)
return trace(argc, argv, verbose);
// if (! do_trace) run interpreter
switch (setjmp(toplevel)) // used to catch multiple events
{
case JMP_GPFAULT:
printf("General Protection violation!\n");
break;
case JMP_BREAK:
printf("break\n");
signal(SIGINT, break_handler);
// what if this is trace process??
break;
}
for (;;)
{
printf("$ ");
gets(buf);
if (toupper(*buf) == 'Q')
break;
// cause one of a number of different GP faults
switch (*buf)
{
case '0': x = *((int far *) 0L); break; // GP fault
case '1': x = *((int far *) -1L); break; // GP fault
case '2': *((char *) main) = 'x'; break; // bashes data!
case '3': x = VioWrtTTY(0L, 100, 0); break; // GP fault in DLL
case '4': x = VioWrtTTY(-1L, 100, 0); break; // GP fault in DLL
case '5': x = VioWrtTTY(0L); break; // boom!
case '6': x = puts(-1L); break; // GP fault
case '7': x = DosGetInfoSeg(1L, 2L); break; // thread dies
}
printf("Executed statement %c\n", *buf);
}
return 0;
}
/* case 2 is important because, though the operation is illegal, it does
not generate a GP fault. Consequently, the operation is successful.
Depending on how OS2TRACE.C is compiled, sometimes a string gets bashed
so that string prints out "General protection vixlation," sometimes
something else gets bashed so that when we exit, C run-time puts up
message about null pointer assignment.
*/
LOCAL WORD NEAR trace(int argc, char *argv[], BOOL verbose)
{
start_trace(progname(argv[0]), cmdline(argc, argv));
/* DosPTrace event loop */
for (;;)
{
ptb.tid = 0;
ptb.cmd = GO;
DosPTrace(&ptb);
switch (ptb.cmd)
{
case EVENT_GP_FAULT:
if (verbose)
{
printf("GP fault (error %04X) at %04X:%04X\n",
ptb.value, ptb.segv, ptb.offv);
print_regs();
}
send_sig_segv();
break;
case EVENT_THREAD_DEAD:
if (verbose)
printf("Thread %u dying\n", ptb.tid);
break;
case EVENT_DYING:
if (verbose)
printf("Process %u dying\n", ptb.pid);
return 0;
}
}
/*NOTREACHED*/
}
LOCAL VOID NEAR print_regs(void)
{
ptb.cmd = READ_REGISTERS;
DosPTrace(&ptb);
printf("AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X BP=%04X\n",
ptb.rAX, ptb.rBX, ptb.rCX, ptb.rDX, ptb.rSI, ptb.rDI, ptb.rBP);
printf("DS=%04X ES=%04X IP=%04X CS=%04X FL=%04X SP=%04X SS=%04X\n",
ptb.rDS, ptb.rES, ptb.rIP, ptb.rCS, ptb.rF, ptb.rSP, ptb.rSS);
}
LOCAL VOID NEAR send_sig_segv(void)
{
/* because of OS/2 "disjoint LDT space," we could just as easily say:
WORD cs = FP_SEG((void far *) catch_sig_segv);
it will be mapped to same selector in both processes
*/
WORD cs = selector_from_segment(1); // catch_sig_segv() in seg 1
WORD ip = FP_OFF((void far *) catch_sig_segv);
set_csip(cs, ip);
}
LOCAL WORD NEAR selector_from_segment(WORD seg)
{
ptb.value = seg;
ptb.cmd = SEG_NUM_TO_SELECTOR;
DosPTrace(&ptb);
return (ptb.cmd == EVENT_ERROR) ? 0 : ptb.value;
}
LOCAL VOID NEAR set_csip(WORD cs, WORD ip)
{
ptb.cmd = READ_REGISTERS;
DosPTrace(&ptb);
ptb.rCS = cs;
ptb.rIP = ip;
if (ptb.rDS != ptb.rSS)
{
printf("Faulted inside DLL code\n");
ptb.rDS = ptb.rSS; // very important!
}
ptb.cmd = WRITE_REGISTERS;
DosPTrace(&ptb);
}
LOCAL VOID NEAR catch_sig_segv(void)
{
longjmp(toplevel, JMP_GPFAULT);
}
// shared by debugger and debuggee processes
LOCAL int break_handler(void)
{
signal(SIGINT, SIG_IGN);
longjmp(toplevel, JMP_BREAK);
}
LOCAL VOID NEAR start_trace(char *prog, char *cmdline)
{
RESULTCODES resc;
if (DosExecPgm(NULL, 0, EXEC_TRACE, cmdline, NULL, &resc, prog) != 0)
return;
ptb.pid = resc.codeTerminate;
ptb.tid = 0;
}
// tacks .EXE after program name
LOCAL char *progname(char *s)
{
static char str[128];
strcpy(str, s);
strcat(str, ".EXE");
return str;
}
// undoes all argc/argv work, appends -x switch
LOCAL char *cmdline(int argc, char *argv[])
{
static char str[128], *t = str;
char *s = argv[0];
register int arg = 0;
while (arg < argc)
{
while (*s)
*t++ = *s++;
*t++ = (arg) ? ' ' : '\0'; // '\0' after program name
s = argv[++arg];
}
*t++ = '-'; *t++ = 'x'; // append -x switch
*t = '\0';
return str;
}
[LISTING SEVEN]
// ptrace.h
// DosPTrace commands
#define READ_I_SPACE 0x0001
#define READ_D_SPACE 0x0002
#define READ_REGISTERS 0x0003
#define WRITE_I_SPACE 0x0004
#define WRITE_D_SPACE 0x0005
#define WRITE_REGISTERS 0x0006
#define GO 0x0007
#define TERMINATE_CHILD 0x0008
#define SINGLE_STEP 0x0009
#define STOP_CHILD 0x000A
#define FREEZE_CHILD 0x000B
#define RESUME_CHILD 0x000C
#define SEG_NUM_TO_SELECTOR 0x000D
#define GET_FLOATINGPT_REGS 0x000E
#define SET_FLOATINGPT_REGS 0x000F
#define GET_DLL_NAME 0x0010
#define THREAD_STATUS 0x0011 // new
#define MAP_READONLY_ALIAS 0x0012
#define MAP_READWRITE_ALIAS 0x0013
#define UNMAP_ALIAS 0x0014
// DosPTrace events
#define EVENT_SUCCESS 0
#define EVENT_ERROR -1
#define EVENT_SIGNAL -2
#define EVENT_SINGLESTEP -3
#define EVENT_BREAKPOINT -4
#define EVENT_PARITYERROR -5
#define EVENT_DYING -6
#define EVENT_GP_FAULT -7
#define EVENT_LOAD_DLL -8
#define EVENT_FLOATPT_ERROR -9
#define EVENT_THREAD_DEAD -10
#define EVENT_ASYNC_STOP -11
#define EVENT_NEW_PROCESS -12
#define EVENT_ALIAS_FREE -13
// DosPTrace error types
#define ERROR_BAD_COMMAND 1
#define ERROR_CHILD_NOTFOUND 2
#define ERROR_UNTRACEABLE 5
// Thread states
#define THREAD_RUNNABLE 0
#define THREAD_SUSPENDED 1
#define THREAD_BLOCKED 2
#define THREAD_CRITSEC 3
// Thread debug states
#define THREAD_THAWED 0
#define THREAD_FROZEN 1
Figurσ 1║ ┴ samplσ sessioε witΦ ß 3▓-bi⌠ versioε oµ thσ G╨ Faul⌠ ì
interpreter
C:\PHARLAP>run386 gpf386
'Q' to quit, '!' to reinstall default GP fault handler
0014:000038a4 is a legal address to poke
0014:000038a0 is not a legal address to poke
$ 1234:00005678 66666666
Protection violation at 000C:00000111!
Error code 1234è <DS 0014> <ES 000C> <FS 0014> <GS 0014>
<EDI 00003CC0> <ESI 66666666> <FLAGS 00010297>
<EAX 00005678> <EBX 00005678> <ECX 00001234> <EDX 00001234>
$ 000c:00000111 66666666
Protection violation at 000C:00000127!
<DS 0014> <ES 000C> <FS 000C> <GS 0014>
<EDI 0000000C> <ESI 66666666> <FLAGS 00010212>
<EAX 00000019> <EBX 00000111> <ECX 0000000C> <EDX 0000000C>
$ 0034:000b80a0 70217021
poked 0034:000b80a0 with 70217021
Figurσ 2║ ┴ samplσ sessioε usinτ thσ OS/▓ G╨ Faul⌠ interpreterè
C:\OS2\PTRACE>os2trace -v
$ 0 ;; x = *((int far *) 0L);
GP fault (error 0000) at 0047:01C0
AX=0030 BX=0000 CX=0000 DX=0087 SI=0087 DI=0001 BP=0CBE
DS=0087 ES=0000 IP=01C0 CS=0047 FL=2246 SP=0C5A SS=0087
General Protection violation!
$ 1 ;; x = *((int far *) -1L);
GP fault (error FFFC) at 0047:01BE
AX=0031 BX=FFFF CX=0000 DX=0087 SI=0087 DI=0001 BP=0CBE
DS=0087 ES=0087 IP=01BE CS=0047 FL=2202 SP=0C5A SS=0087
General Protection violation!
$ 2 ;; *((char *) main) = 'x';
Executed statement 2
$ 3 ;; x = VioWrtTTY(0L, 100, 0);
GP fault (error 0000) at 00D7:27F1
AX=0000 BX=02C4 CX=0051 DX=0000 SI=0087 DI=0051 BP=0C34
DS=00DF ES=0000 IP=27F1 CS=00D7 FL=2246 SP=0C26 SS=0087
Faulted inside DLL code
General Protection vxolation!
$ 7 ;; x = DosGetInfoSeg(1L, 2L);
Thread 1 dying
Process 79 dying
C:\OS2\PTRACE>
Figurσ 3║ Aε OS/▓ message¼ differen⌠ froφ norma∞ G╨ faul⌠ dump
Session Title: CVP app OS2TRACE.EXE
The system detected a general protection
fault (trap D) in a system call.
CS=0047 IP=0237 S=0087 SP=0C5A
ERRCD=0000 ERLIM=**** ERACC=**
Arguments used in system call (high to low):
0000 0001 0000 0002
End the program
Figurσ 4║ Detectinτ IN╘ 0├ unde≥ OS/2
SYS1942: A program attempted to reference storage
outside the limits of a stack segment. The program
was ended.
TRAP 000C